package csci6450.chickenFriedFS.nuggetCore;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *  <p style="margin-top: 0">
 *        Should be singleton.  We want to make sure we have one and only one 
 *        Nugget Log instance at any given time.
 *      </p>
 */
public abstract class NuggetLog implements Serializable {

    protected ArrayList<Nugget> nuggetList = new ArrayList<Nugget>();
    private String logLocation;
    protected ArrayList<Nugget> latestNuggets = new ArrayList<Nugget>();
    private FileOutputStream writeToFile;

    public void init() throws FileNotFoundException, IOException, ParseException {
        File nuggetLog = new File(this.getLogLocation());
        if (nuggetLog.exists()) {
            System.out.println("Nugget Log already exists. Load it up at "+nuggetLog.getAbsolutePath());
            this.readLog(nuggetLog);
        } else {
            System.out.println("Nugget Log does not exisT. Make it.");
            nuggetLog.getParentFile().mkdirs();
            nuggetLog.createNewFile();
        }
        writeToFile = new FileOutputStream(nuggetLog);
    }

    /**
     * The primary log location.
     * @return
     */
    public String getLogLocation() {
        return logLocation;
    }

    /**
     * Sets the primary log location.
     * @param val
     */
    public void setLogLocation(String val) {
        this.logLocation = val;
    }

    /**
     * This function will erase the current state, wiping the log files out and
     * the information stored in memory in this object. Starts fresh.
     * @return
     */
    public boolean format() {
        this.nuggetList.clear();
        this.latestNuggets.clear();
        File log = new File(this.logLocation);
        boolean success = log.delete();
        try {
            this.init();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(NuggetLog.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(NuggetLog.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParseException ex) {
            Logger.getLogger(NuggetLog.class.getName()).log(Level.SEVERE, null, ex);
        }
        return success;
    }

    /**
     *  Returns a clone() of the list. Note: Any updates to returned list will 
     *  not affect the nuggetList in memory or the nugget log file. (Should we 
     *  have a way to iterate the nugget list in the nuggetList instead of a 
     *  getNuggetList.)
     */
    public ArrayList<Nugget> getNuggetList() {
        return (ArrayList<Nugget>) nuggetList.clone();
    }

    /**
     * Removes nugget&lt;code&gt;File&lt;/code&gt; from the nugget. Adds nugget to 
     * nuggetList. Writes nugget info to log file. Syncronized.
     */
    public boolean addNugget(Nugget nugget) {
        try {
            //clear the nugget
            nugget.setNugget(null);
            this.nuggetList.add(nugget);
            this.latestNuggets.add(nugget);
            this.writeToLog(this.getLogString(nugget) + NuggetConstants.ADD + NuggetConstants.NEWLINE);
            return true;
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public boolean removeNugget(Nugget nugget) {
        try {
            this.nuggetList.remove(nugget);
            this.latestNuggets.remove(nugget);
            this.writeToLog(this.getLogString(nugget) + NuggetConstants.REMOVE + NuggetConstants.NEWLINE);
            return true;
        } catch (IOException ex) {
            Logger.getLogger(NuggetLog.class.getName()).log(Level.SEVERE, null, ex);
            if (!this.nuggetList.contains(nugget)) {
                this.nuggetList.add(nugget);
            }
        }
        return false;
    }

    /**
     *  syncronized. Contains the List of latestNuggets since the last time it
     *  was called.  It then removes all nuggets from the list.
     */
    public ArrayList<Nugget> getLatestNuggets() {
        ArrayList<Nugget> latest = (ArrayList<Nugget>) latestNuggets.clone();
        this.latestNuggets.clear();
        return latest;
    }

    /**
     * This will over write the existing log file and write out the current state.
     * Could be used to refresh the log if it gets large so that it will not
     * take as long to reread it back in.
     */
    public void writeLog() throws FileNotFoundException, IOException {
        File temp = new File(this.stripExtension() + ".tmp");
        if(temp.exists()){
            temp.delete();
        }
        temp.getParentFile().mkdirs();
        temp.createNewFile();
        FileOutputStream out = new FileOutputStream(temp);
        for (int i = 0; i < this.nuggetList.size(); i = i + 1) {
            out.write((this.getLogString(this.nuggetList.get(i)) + NuggetConstants.ADD+NuggetConstants.NEWLINE).getBytes());
        }
        out.close();
        this.writeToFile.close();

        //rename the current file to have a .old extension
        File current = new File(this.logLocation);
        File old = new File(this.stripExtension() + ".old");
        if (old.exists()) {
            old.delete();
        }
        current.renameTo(old);

        //rename the temp file to the current log
        File newCurrent = new File(this.logLocation);
        temp.renameTo(newCurrent);

        //delete the temp file
        File deleteTemp = new File(this.stripExtension() + ".tmp");
        if (deleteTemp.exists()) {
            deleteTemp.delete();
        }
    }

    /**
     * This will append to the log file.
     *
     * @param add
     * @throws IOException
     */
    private void writeToLog(String add) throws IOException {
        this.writeToFile.write(add.getBytes());
    }

    // <editor-fold defaultstate="collapsed" desc=" UML Marker ">
    // #[regen=yes,id=DCE.035741C9-9523-C849-123E-2F6F4ADFB754]
    // </editor-fold>
    private void readLog(File file) throws FileNotFoundException, IOException, ParseException {
        BufferedReader in = new BufferedReader(new FileReader(file));
        String str;
        while ((str = in.readLine()) != null) {
            processLine(str);
        }
        in.close();

    }

    private String stripExtension() {
        int lastDot = this.getLogLocation().lastIndexOf(".");
        if (lastDot != -1) {
            return this.getLogLocation().substring(0, lastDot);
        } else {
            return this.getLogLocation();
        }
    }

    @Override
    public void finalize() {
        try {
            this.writeToFile.close();
        } catch (IOException ex) {
            Logger.getLogger(NuggetLog.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void reset(){
        System.out.println("NuggetSize :="+this.nuggetList.size());
        this.latestNuggets = this.nuggetList;
        System.out.println("LatestNuggetSize :="+this.latestNuggets.size());
    }

    protected abstract String getLogString(Nugget nugget);

    protected abstract void processLine(String line) throws ParseException;
}

